/*
 *                  Find position (line number) of function definition
 *                  ( something like press "[[" in VI ) 
 *
 *                  Author:
 *                          ChungChe Hsieh
 *                          2011/05/14
 *  TODO:
 *                  1. [X] consider  #if 0 #endif
 *
 *  HISTORY:
 *                  1. [O] kill the empty line in _traverse
 *                  ------- 2011/05/15 -------------------------------------
 *                  2. [O] consider block comment 
 *                  3. [O] consider '{' & '}'
 *                  4. [O] consider "  {  " & "  } "
 *                  5. [O] get input line number, and find the position of 
 *                         function.
 *                  --------------------------------------------------------
 *
 */
#include "my_node.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// hard coded... 
// global variables are good!       >///////<
struct node nd[1000];

enum{
    NOTSET_BEGIN = -1,
    NOTSET_END = -1,
    NOTSET_NEIB = -1,
    NOTSET_CHILD = -1,
    NOTSET_PARENT = -1,
    TOP = 0,
};

#if 0
test
{
    test{
    }
}
test1
{
}
#endif

// {{{{
static void set(int ndIdx, int begin, int end, int child, int parent)
{
	if(begin != -1)
        nd[ndIdx]._begin = begin;
	if(end != -1)
        nd[ndIdx]._end = end;
	if(child != -1)
	{
        // New child
        if(nd[ndIdx]._childNum == 0)
        {
		    nd[ndIdx]._child = child;
        }
        else // Child exist
        {	
            int i;
            int ptr = nd[ndIdx]._child;
            for(i=0;i < nd[ndIdx]._childNum-1; ++i)
                ptr = nd[ptr]._right;
            nd[ptr]._right = child;
        }
        nd[ndIdx]._childNum++; 
    }
    if(parent != -1)
        nd[ndIdx]._parent = parent;
}
static void _init()
{
    memset(nd,0,sizeof(nd));
}
static void _traverse(int idx, int depth)
{
    // terminate
    if(idx==0)
        return;
    int i;
    printf("-- %2d(%3d,%3d) ", idx, nd[idx]._begin, nd[idx]._end);
    if(nd[idx]._child==0)
        printf("\n");
    _traverse(nd[idx]._child, depth+1);

    for(i=0;i<depth;++i)
    {   
        if(nd[idx]._right!=0)
            printf("               ");
    }
    _traverse(nd[idx]._right, depth);
}
static void _print_first_level(int idx)
{
    int i;
    int child = nd[idx]._child;
    for(i=0;i<nd[idx]._childNum;++i)
    {
        printf("function def%03d = %d\n", i, nd[child]._begin);
        child = nd[child]._right;
    }
}
/*
// * {{{{
 */
//{ //  {{{{{
/* {{
 */
static void _qline(line)
{
    printf("query %d=%d\n", line, query(line));
}
// Type
enum{
    T_SLASH =           0,
    T_ASTERISK,
    T_SINGLE_QUOTE,
    T_DOUBLE_QUOTE,
    T_OPEN_BRACE,
    T_CLOSE_BRACE,
    T_END_OF_LINE,
    T_BACKSLASH,

    T_TOTAL_TYPE,
};
enum{
    NOT_IN_DOUBLE_BRACE=0,
    IS_IN_DOUBLE_BRACE,
};
// Last Status
enum{
    E_NOTHING =         0,
    E_SLASH =           1 << T_SLASH,   
    E_ASTERISK =        1 << T_ASTERISK,
    E_SINGLE_QUOTE =    1 << T_SINGLE_QUOTE,
    E_DOUBLE_QUOTE =    1 << T_DOUBLE_QUOTE,
    E_OPEN_BRACE =      1 << T_OPEN_BRACE,
    E_CLOSE_BRACE =     1 << T_CLOSE_BRACE,
    E_END_OF_LINE =     1 << T_END_OF_LINE,
    E_BACKSLASH =       1 << T_BACKSLASH,
};
// Skip status ( need to skip if the status > 0)
enum
{
    S_NOTHING =             0x0000,
    S_LINE_COMMENT =        0x0001,
    S_BLOCK_COMMENT =       0x0002,
    S_SINGLE_BRACE =        0x0004,
    S_DOUBLE_BRACE =        0x0008,

};
static int _isType(int flag, int type)
{
    return ((flag >> type) & 1);
}
// check if this ch need to check
static int _skip(char ch, int *lastChar, int *status, int *dbflag,int *line)
{
    switch(ch)
    {
        case '/':
            // IF last_ch == / , line comment start
            // IF last_ch == * , block comment end
            switch(*lastChar)
            {
                case E_SLASH:
                    *status |= S_LINE_COMMENT;
                    break;
                case E_ASTERISK:
                    *status &= ~S_BLOCK_COMMENT;
                    break;
            }
            *lastChar = E_SLASH;
            break;
        case '*':
            // IF last_ch == / , block comment start
            if(E_SLASH == *lastChar)
                *status |= S_BLOCK_COMMENT;
            *lastChar = E_ASTERISK;
            break;
        case '\'':
            // IF last_ch == { , single brace end
            // IF last_ch == } , single brace end
            switch(*lastChar)
            {
                case E_OPEN_BRACE:
                    *status &= ~S_SINGLE_BRACE; 
                    break;
                case E_CLOSE_BRACE:
                    *status &= ~S_SINGLE_BRACE; 
                    break;
            }
            *lastChar = E_SINGLE_QUOTE;
            break;
        case '\"':
            // double brace end
            if(E_BACKSLASH == *lastChar)
            {
                *lastChar = E_DOUBLE_QUOTE;
                break;
            } 
            else if(*dbflag == IS_IN_DOUBLE_BRACE)
                *status &= ~S_DOUBLE_BRACE; 
            else if(*dbflag == NOT_IN_DOUBLE_BRACE) // double brace start
                *status |=  S_DOUBLE_BRACE; 
            
            *dbflag = !(*dbflag);
            
            *lastChar = E_DOUBLE_QUOTE;
            break;
        case '{':
            // IF last_ch == ', single brace start
            if(E_SINGLE_QUOTE == *lastChar)
                *status |= S_SINGLE_BRACE;
            *lastChar = E_OPEN_BRACE;
            break;
        case '}':
            // IF last_ch == ', single brace start
            if(E_SINGLE_QUOTE == *lastChar)
                *status |= S_SINGLE_BRACE;
            *lastChar = E_CLOSE_BRACE;
            break;
        case '\n':
            // If line comment
            *line=*line+1;
            if(*status == S_LINE_COMMENT)
                *status &= ~S_LINE_COMMENT;
            *lastChar = E_END_OF_LINE;
            break;
        case '\\':
            *lastChar = E_BACKSLASH;
            break;
        default:
            *lastChar = E_NOTHING;
    }
    return *status;
}
void scanbuffer(char *buffer, int size)
{
    if(buffer==NULL)
        return;
    if(size<=0)
        return;
    int i; // iter
    int ptr=0; // buffer index
    int line=1; // current parsing line
    int newNode=0; // current node created {{{
    int currNode=0; 
    int take_care =0;
    int skip_line_comment=0;
    int skip_block_comment=0;
    int flag = 0;
    int status = S_NOTHING;
    int dbflag = 0;// double brace flag
    // Init here {{{{{{{{{{
    _init(); 
    // Set root node
    set(TOP, 0, size, NOTSET_CHILD, NOTSET_PARENT);
    //printf("skip ");
    while(ptr<size)
    {
        // handle flag
        int result= _skip(buffer[ptr], &flag, &status,&dbflag,&line);
        //printf("%c(%d) ", buffer[ptr],result);
        if( result > 0)
        {
            ptr++;
            continue;
        }
        switch(buffer[ptr])
        {
            case '{':
                // 1. prepare child node
                newNode++;
                set(newNode, line, NOTSET_END, NOTSET_CHILD, currNode);
                // 2. set current node
		        set(currNode, NOTSET_BEGIN, NOTSET_END , newNode, NOTSET_PARENT);
                // move to child position
                currNode=newNode;
                break;
            case '}':
                // set curr node's end
                set(currNode, NOTSET_BEGIN, line, NOTSET_CHILD, NOTSET_PARENT);
                // back to parent position
                currNode=nd[currNode]._parent;
                break;
        }
        ptr++;
    }
    //printf("\n");
    // Set last node
    set(currNode, NOTSET_BEGIN, line, NOTSET_CHILD, NOTSET_PARENT);
    // Summary
    printf("newNode=%d\n", newNode);
    printf("currNode=%d\n", currNode);
    _traverse(nd[TOP]._child, 0);
    _print_first_level(TOP);
    _qline(146);
    _qline(174);
    _qline(94);
    _qline(22);
    _qline(182);
    _qline(180);
    _qline(193);
    _qline(line-1);

}
int query(int line)
{
    int i;
    int res=0;
    int ptr=nd[TOP]._child;
    for(i=0;i<nd[TOP]._childNum;++i)
    {
        if(line <= nd[ptr]._end)
        {
            res = nd[ptr]._begin;
            if(res>line)
                return 0;
            return res;
        }
        ptr=nd[ptr]._right;
    }
    return 0;
}
